# LeetCode 530、二叉搜索树的最小绝对差

# 一、题目描述

给你一个二叉搜索树的根节点 root ,返回 树中任意两不同节点值之间的最小差值

差值是一个正数,其数值等于两值之差的绝对值。

# 二、题目解析

# 三、参考代码

# 1、Java 代码

// 登录 AlgoMooc 官网获取更多算法图解
// https://www.algomooc.com
// 作者:程序员吴师兄
// 代码有看不懂的地方一定要私聊咨询吴师兄呀
// 二叉搜索树的最小绝对差( LeetCode 530 ): https://leetcode-cn.com/problems/minimum-absolute-difference-in-bst/submissions/
class Solution {
    public int getMinimumDifference(TreeNode root) {

        // 设置一个变量用来记录两不同节点值之间的差值
        int result =  Integer.MAX_VALUE;

        // 设置一个栈,用来保存路径
        Stack<TreeNode> stack = new Stack<>();

        // 设置一个节点,一开始指向根节点
        TreeNode curNode = root;
        
        // 设置一个节点 preNode ,用来记录当前遍历节点的上一个节点
        TreeNode preNode = null;

        // 设置三种状态,方便表示当前遍历当前节点的时候进行到哪一步了
        // 每个节点都有 左、右、上 这三种状态
        // 按照 左 --> 右 --> 上 的顺序观察每个节点

        // 左代表该节点的左右孩子节点都没有遍历
        int nodeLeft = 100;

        // 右代表该节点的左孩子节点已经遍历,右孩子节点还没有遍历
        int nodeRight = 200;

        // 上代表左右孩子节点都已经遍历,需要返回到它的父节点
        int nodeUp = 300;

        // 每个节点的初始化状态都是从 左 开始
        int nodeState = nodeLeft;


        // 对二叉树进行遍历
        while(curNode != null){

            // 位置 ① 

            // 如果当前节点的状态是【左】,说明该节点的左右孩子节点都没有遍历
            if(nodeState == nodeLeft){

                // 如果当前节点有左子树
                if(curNode.left != null){

                    // 先把当前节点加入到栈中,用来记录节点移动的路径
                    stack.push(curNode);

                    // 开始观察当前节点的左孩子节点,代码来到了位置 ① 
                    curNode = curNode.left;
                }else{

                    // 如果当前节点没有左子树,切换当前节点的状态为【右】
                    // 代码来到了位置 ① 
                    nodeState = nodeRight;
                }

            }else if(nodeState == nodeRight){ // 如果当前节点的状态是【右】,说明该节点的左孩子节点已经遍历,右孩子节点还没有遍历

                // 二叉树中序遍历的处理结果
                if(preNode != null){
                    result = Math.min(result,curNode.val - preNode.val);
                }

                preNode = curNode;

                // 如果当前节点有右子树
                if(curNode.right != null){

                    // 先把当前节点加入到栈中,用来记录节点移动的路径
                    stack.push(curNode);

                    // 开始观察当前节点的右孩子节点
                    curNode = curNode.right;

                    // 每个节点开始的状态都是【左】开始,所以需要重新设置一下节点的状态
                    nodeState = nodeLeft;

                    // 执行完上面三行代码,代码来到了位置 ①     

                }else{
                    // 如果当前节点没有右子树,切换当前节点的状态为【上】
                    // 代码来到了位置 ①
                    nodeState = nodeUp;
                }
            }else if(nodeState == nodeUp){ // 如果当前节点的状态是【上】,说明左右孩子节点都已经遍历,需要返回到它的父节点

                // 需要返回到当前节点的父节点位置,通过栈顶元素来获取当前节点的父节点
                // 首先构建一个空的节点
                TreeNode parent = null;

                // 如果栈中有元素
                if(!stack.isEmpty()){

                    // 那么,栈顶元素就是当前节点的父节点
                    parent = stack.pop();

                    // 判断一下父节点的左节点是否为当前节点
                    // 比如这颗二叉树
                    //           1
                    //         /   \
                    //        2     3
                    //       / \     \
                    //      4   5     6
                    //  如果当前节点是 4 ,那么 4 的父节点是 2,2 的左孩子节点是 4,此时需要切换状态为【右】
                    //  如果当前节点是 5 ,那么 5 的父节点是 2,2 的左孩子节点是 4,此时不需要切换状态

                    // 如果父节点的左节点为当前节点
                    if(parent.left == curNode){
                        // 切换当前节点的状态为【右】
                        nodeState = nodeRight;
                    }
                }
                // 开始观察当前节点的父节点
                // 如果上述代码中栈中有元素,那么 parent 有值,代码来到了位置 ①
                // 如果上述代码中栈中没有元素,那么 parent 没有值,会跳出循环
                curNode = parent;

            }
        }
        // 返回结果
        return result;
    }
}

# **2、**C++ 代码

// 登录 AlgoMooc 官网获取更多算法图解
// https://www.algomooc.com
// 作者:程序员吴师兄
// 代码有看不懂的地方一定要私聊咨询吴师兄呀
// 二叉搜索树的最小绝对差( LeetCode 530 ): https://leetcode-cn.com/problems/minimum-absolute-difference-in-bst/submissions/
class Solution
{
public:
    int getMinimumDifference(TreeNode* root)
    {

        // 设置一个变量用来记录两不同节点值之间的差值
        int result = INT_MAX;

        // 设置一个栈,用来保存路径
        stack<TreeNode*> st;

        // 设置一个节点,一开始指向根节点
        TreeNode* curNode = root;

        // 设置一个节点 preNode ,用来记录当前遍历节点的上一个节点
        TreeNode* preNode = NULL;

        // 设置三种状态,方便表示当前遍历当前节点的时候进行到哪一步了
        // 每个节点都有 左、右、上 这三种状态
        // 按照 左 --> 右 --> 上 的顺序观察每个节点

        // 左代表该节点的左右孩子节点都没有遍历
        int nodeLeft = 100;

        // 右代表该节点的左孩子节点已经遍历,右孩子节点还没有遍历
        int nodeRight = 200;

        // 上代表左右孩子节点都已经遍历,需要返回到它的父节点
        int nodeUp = 300;

        // 每个节点的初始化状态都是从 左 开始
        int nodeState = nodeLeft;

        // 对二叉树进行遍历
        while (curNode != NULL)
        {

            // 位置 ①

            // 如果当前节点的状态是【左】,说明该节点的左右孩子节点都没有遍历
            if (nodeState == nodeLeft)
            {

                // 如果当前节点有左子树
                if (curNode->left != NULL)
                {

                    // 先把当前节点加入到栈中,用来记录节点移动的路径
                    st.push(curNode);

                    // 开始观察当前节点的左孩子节点,代码来到了位置 ①
                    curNode = curNode->left;
                }
                else
                {

                    // 如果当前节点没有左子树,切换当前节点的状态为【右】
                    // 代码来到了位置 ①
                    nodeState = nodeRight;
                }
            }
            else if (nodeState == nodeRight)
            { // 如果当前节点的状态是【右】,说明该节点的左孩子节点已经遍历,右孩子节点还没有遍历

                // 二叉树中序遍历的处理结果
                if (preNode != NULL)
                {
                    result = min(result, curNode->val - preNode->val);
                }

                preNode = curNode;

                // 如果当前节点有右子树
                if (curNode->right != NULL)
                {

                    // 先把当前节点加入到栈中,用来记录节点移动的路径
                    st.push(curNode);

                    // 开始观察当前节点的右孩子节点
                    curNode = curNode->right;

                    // 每个节点开始的状态都是【左】开始,所以需要重新设置一下节点的状态
                    nodeState = nodeLeft;

                    // 执行完上面三行代码,代码来到了位置 ①
                }
                else
                {
                    // 如果当前节点没有右子树,切换当前节点的状态为【上】
                    // 代码来到了位置 ①
                    nodeState = nodeUp;
                }
            }
            else if (nodeState == nodeUp)
            { // 如果当前节点的状态是【上】,说明左右孩子节点都已经遍历,需要返回到它的父节点

                // 需要返回到当前节点的父节点位置,通过栈顶元素来获取当前节点的父节点
                // 首先构建一个空的节点
                TreeNode* parent = NULL;

                // 如果栈中有元素
                if (!st.empty())
                {

                    // 那么,栈顶元素就是当前节点的父节点
                    parent = st.top(); 
                    st.pop();

                    // 判断一下父节点的左节点是否为当前节点
                    // 比如这颗二叉树
                    //           1
                    //         /   \
                    //        2     3
                    //       / \     \
                    //      4   5     6
                    //  如果当前节点是 4 ,那么 4 的父节点是 2,2 的左孩子节点是 4,此时需要切换状态为【右】
                    //  如果当前节点是 5 ,那么 5 的父节点是 2,2 的左孩子节点是 4,此时不需要切换状态

                    // 如果父节点的左节点为当前节点
                    if (parent->left == curNode)
                    {
                        // 切换当前节点的状态为【右】
                        nodeState = nodeRight;
                    }
                }
                // 开始观察当前节点的父节点
                // 如果上述代码中栈中有元素,那么 parent 有值,代码来到了位置 ①
                // 如果上述代码中栈中没有元素,那么 parent 没有值,会跳出循环
                curNode = parent;
            }
        }
        // 返回结果
        return result;
    }
};

# 3、Python 代码

登录 AlgoMooc 官网获取更多算法图解
# https://www.algomooc.com
# 作者:程序员吴师兄
# 代码有看不懂的地方一定要私聊咨询吴师兄呀
# 二叉搜索树的最小绝对差( LeetCode 530 ): https://leetcode-cn.com/problems/minimum-absolute-difference-in-bst/submissions/
class Solution:
     def getMinimumDifference(self, root: TreeNode) -> int:

        # 设置一个数组用来保存二叉树前序遍历的结果
        result = 100000


        # 设置一个栈,用来保存路径
        stack = list()

        # 设置一个节点,一开始指向根节点
        curNode = root

       
        # 设置一个节点 preNode ,用来记录当前遍历节点的上一个节点

        preNode = None

        # 设置三种状态,方便表示当前遍历当前节点的时候进行到哪一步了
        # 每个节点都有 左、右、上 这三种状态
        # 按照 左 --> 右 --> 上 的顺序观察每个节点

        # 左代表该节点的左右孩子节点都没有遍历
        nodeLeft = 100

        # 右代表该节点的左孩子节点已经遍历,右孩子节点还没有遍历
        nodeRight = 200

        # 上代表左右孩子节点都已经遍历,需要返回到它的父节点
        nodeUp = 300

        # 每个节点的初始化状态都是从 左 开始
        nodeState = nodeLeft


        # 对二叉树进行遍历
        while curNode :

            # 位置 ① 

            # 如果当前节点的状态是【左】,说明该节点的左右孩子节点都没有遍历
            if nodeState == nodeLeft :
               

                # 如果当前节点有左子树
                if curNode.left :

                    # 先把当前节点加入到栈中,用来记录节点移动的路径
                    stack.append(curNode)

                    # 开始观察当前节点的左孩子节点,代码来到了位置 ① 
                    curNode = curNode.left
                else:

                    # 如果当前节点没有左子树,切换当前节点的状态为【右】
                    # 代码来到了位置 ① 
                    nodeState = nodeRight


            elif nodeState == nodeRight:

                # 如果当前节点的状态是【右】,说明该节点的左孩子节点已经遍历,右孩子节点还没有遍历

                if preNode :
                   result = min(result,curNode.val - preNode.val)

                preNode = curNode

                # 如果当前节点有右子树
                if curNode.right :

                    # 先把当前节点加入到栈中,用来记录节点移动的路径
                    stack.append(curNode)

                    # 开始观察当前节点的右孩子节点
                    curNode = curNode.right

                    # 每个节点开始的状态都是【左】开始,所以需要重新设置一下节点的状态
                    nodeState = nodeLeft

                    # 执行完上面三行代码,代码来到了位置 ①     

                else:
                    # 如果当前节点没有右子树,切换当前节点的状态为【上】
                    # 代码来到了位置 ①
                    nodeState = nodeUp

            elif nodeState == nodeUp : 

                # 如果当前节点的状态是【上】,说明左右孩子节点都已经遍历,需要返回到它的父节点
                # 把当前节点加入到二叉树后序遍历的结果数组中
                # postorderResult.add(node.val)

                # 需要返回到当前节点的父节点位置,通过栈顶元素来获取当前节点的父节点
                # 首先构建一个空的节点
                parent = None

                # 如果栈中有元素
                if stack :

                    # 那么,栈顶元素就是当前节点的父节点
                    parent = stack.pop()

                    # 判断一下父节点的左节点是否为当前节点
                    # 比如这颗二叉树
                    #           1
                    #         /   \
                    #        2     3
                    #       / \     \
                    #      4   5     6
                    #  如果当前节点是 4 ,那么 4 的父节点是 2,2 的左孩子节点是 4,此时需要切换状态为【右】
                    #  如果当前节点是 5 ,那么 5 的父节点是 2,2 的左孩子节点是 4,此时不需要切换状态

                    # 如果父节点的左节点为当前节点
                    if parent.left == curNode :
                        # 切换当前节点的状态为【右】
                        nodeState = nodeRight

                # 开始观察当前节点的父节点
                # 如果上述代码中栈中有元素,那么 parent 有值,代码来到了位置 ①
                # 如果上述代码中栈中没有元素,那么 parent 没有值,会跳出循环
                curNode = parent


        # 根据题目要求,返回二叉树前序、中序、后序遍历的结果
        return result